﻿/* 
   Copyright 2011 LUCA Studios LLC & John Evans

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System.Windows.Controls;

namespace LUCAStudios.Silverlight.Controls
{
    using System;
    using System.Collections.Specialized;
    using System.Linq;
    using System.Windows;
    using System.Windows.Markup;
    using System.Collections.Generic;
    using DragDrop;

    /// <summary>
    /// A window control that displays LUCATabItem controls 
    /// </summary>
    [TemplateVisualState(Name = "Normal", GroupName = "WindowDisplayMode")]
    [TemplateVisualState(Name = "NoUI", GroupName = "WindowDisplayMode")]
    [TemplateVisualState(Name = "Docked", GroupName = "WindowDisplayMode")]
    [TemplatePart(Name=CloseButton, Type=typeof(Button))]
    [ContentProperty("Children")]
    public class LUCATabWindow : WindowBase, IDragDropTarget
    {
        private Button _closeButton;
        private const string CloseButton = "btnCloseWindow";

        /// <summary>
        /// Constructor
        /// </summary>
        public LUCATabWindow()
        {
            DefaultStyleKey = typeof(LUCATabWindow);
        }

        #region events

        public event EventHandler WindowClosing;
        
        #endregion

        #region private

        /// <summary>
        /// Called when the LUCA control is loaded
        /// </summary>
        protected override void OnLoaded()
        {
            base.OnLoaded();

            if (Children.Any())
                UpdateToContentItem(Children.Last() as LUCATabItem);
        }

        /// <summary>
        /// Sets the CurrentContent property to the last .ItemContent in the list of LUCAWindowItems
        /// </summary>
        internal void UpdateToContentItem(LUCATabItem item)
        {
            if (item == default(LUCATabItem)) throw new ArgumentNullException("item");


            if (!(item.Content.Equals(CurrentContent)))
            {
                //Logger.InsertEntry("LUCAWindow", "Content Updated to: " + item.Title);
                CurrentContent = item.Content;
            }

            CloseButtonVisibility = Children.Count == 1 ? Visibility.Collapsed : Visibility.Visible;
        }

        /// <summary>
        /// Swaps the display position of two given LUCATabItem's in the collection.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="target"></param>
        internal void SwapItems(LUCATabItem source, LUCATabItem target)
        {
            if (source == default(LUCATabItem)) throw new ArgumentNullException("source");
            if (target == default(LUCATabItem)) throw new ArgumentNullException("target");

            int iA = Children.IndexOf(source);
            int iB = Children.IndexOf(target);

            if (iA == -1 || iB == -1)
                throw new LUCAControlException("LUCAWindowItem not found in collection of parent container.");

            RemoveChild(source);

            if (iB == Children.Count)
                AddChild(source);
            else
                InsertChild(iA > iB ? iB : iB + 1, source);
        }

        /// <summary>
        /// Takes any LUCAWindowItems from the source window and places them in the target window
        /// </summary>
        /// <param name="target"></param>
        internal void MoveItems(LUCATabWindow target)
        {
            if (target == null) throw new ArgumentNullException("target");

            CurrentContent = null;

            while (Children.Any())
            {
                var i = Children.First();
                RemoveChild(i);
                target.AddChild(i);
            }
        }

        /// <summary>
        /// Called when the Children collection changes
        /// </summary>
        /// <param name="e"></param>
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {

            if (e.NewItems != null)
            {
                UpdateToContentItem(e.NewItems[e.NewItems.Count - 1] as LUCATabItem);
            }

            if (e.OldItems != null)
            {
                foreach (var ci in e.OldItems.OfType<LUCATabItem>())
                {
                    ci.SetCheckedState(false);
                }

                if (Children.Any())
                {
                    UpdateToContentItem(Children.Last() as LUCATabItem);
                    var li = Children.LastOrDefault() as LUCATabItem;
                    if (li != default(LUCATabItem))
                        li.SetCheckedState(true);
                }else
                {
                    CurrentContent = null;
                }
            }

            base.OnCollectionChanged(e);
        }

        private void InitEventHooks()
        {
            if (_closeButton == null) throw new LUCAControlException("Template control not assigned as expected.");

            _closeButton.Click += closeButton_Click;
        }

        private void closeButton_Click(object sender, RoutedEventArgs e)
        {
            if (WindowClosing != null) WindowClosing(this, new EventArgs());

            OnWindowClosing();
        }

        /// <summary>
        /// When overridden in a derived class, allows the implementor to 
        /// handle window closing activity manually
        /// </summary>
        protected virtual void OnWindowClosing()
        {
            //remove this control and do not preserve the children as part of the control
            this.Remove(false);
        }

        #endregion

        #region dependency properties

        /// <summary>
        /// Backing property for the CloseButtonVisibility property.  
        /// </summary>
        internal static readonly DependencyProperty CloseButtonVisibilityProperty =
            DependencyProperty.Register("CloseButtonVisibility", typeof(Visibility), typeof(LUCATabWindow), new PropertyMetadata(Visibility.Visible));

        /// <summary>
        /// Dependency property that determines whether the window close button is visible or not.  
        /// Typically you'd want this to be visible, since
        /// Tab Windows are very transient by nature.  The control also handles visibility 
        /// changes when there is only tab left vs. >1 tab.  So it's best not to play with this too much.
        /// </summary>
        internal Visibility CloseButtonVisibility
        {
            get { return (Visibility)GetValue(CloseButtonVisibilityProperty); }
            set { SetValue(CloseButtonVisibilityProperty, value); }
        }


        /// <summary>
        /// Backing property for the AllowAttach Dependency Property
        /// </summary>
        public static readonly DependencyProperty AllowAttachToProperty =
            DependencyProperty.Register("AllowAttachTo", typeof(bool), typeof(LUCATabWindow),
                                        new PropertyMetadata(true));

        /// <summary>
        /// Backing property for the AllowDetachFrom Dependency Property
        /// </summary>
        public static readonly DependencyProperty AllowDetachFromProperty =
            DependencyProperty.Register("AllowDetachFrom", typeof(bool), typeof(LUCATabWindow),
                                        new PropertyMetadata(true));

        /// <summary>
        /// Backing property for the AllowReorder Dependency Property
        /// </summary>
        public static readonly DependencyProperty AllowReorderProperty =
            DependencyProperty.Register("AllowReorder", typeof(bool), typeof(LUCATabWindow), new PropertyMetadata(true));

        /// <summary>
        /// Boolean value indicating whether a LUCAWindowItem may be attached to the window by the user via drag/drop.
        /// </summary>
        public bool AllowAttachTo
        {
            get { return (bool)GetValue(AllowAttachToProperty); }
            set { SetValue(AllowAttachToProperty, value); }
        }

        /// <summary>
        /// Boolean value indicating whether content inside the window may be detached from the window by the user.
        /// </summary>
        public bool AllowDetachFrom
        {
            get { return (bool)GetValue(AllowDetachFromProperty); }
            set { SetValue(AllowDetachFromProperty, value); }
        }

        /// <summary>
        /// Boolean value indicating whether content tabs may be reordered in this window.
        /// </summary>
        public bool AllowReorder
        {
            get { return (bool)GetValue(AllowReorderProperty); }
            set { SetValue(AllowReorderProperty, value); }
        }

        #endregion

        #region overrides

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _closeButton = GetTemplateChild(CloseButton) as Button;

            InitEventHooks();
        }

        /// <summary>
        /// Requires the control to return drop zones when a drag operation begins
        /// </summary>
        /// <param name="sourceData">The IDragDropSource.DragDropData information from the object that was dropped</param>
        /// <returns></returns>
        public override IEnumerable<DropZoneBase> GetDropZones(object sourceData)
        {
            var l = new List<DropZoneBase>();
            var gt = TransformToVisual(LayoutContext.RootVisual);
            var p = gt.Transform(new Point(0, 0));
            var wd = new Rect(p.X, p.Y, ActualWidth, ActualHeight);

            if (VisualState == WindowLayoutState.Docked || VisualState == WindowLayoutState.Floating)
            {
                //only accepts other LUCATabWindow types
                if (AllowAttachTo && sourceData is LUCATabWindow)
                {
                    l.Add(new UpperHighlightingDropZone(this, wd, new Thickness(0, 10, 0, 0)));
                }
            }

            l.AddRange(base.GetDropZones(sourceData));
            return l;
        }

        /// <summary>
        /// Called when a IDragDropSource is dropped on the IDragDropTarget (in any defined drop zone {see 'GetDropZones() method'})
        /// </summary>
        /// <param name="dropZone">The dropzone object that the IDragDropSource dropped into</param>
        /// <param name="dropData">The data associated with the IDragDropSource.DragDropData.  Usually the IDragDropSource object itself.</param>
        public override void OnDrop(DropZoneBase dropZone, object dropData)
        {
            var win = dropData as LUCATabWindow;
            if (win != null && dropZone is UpperHighlightingDropZone)
            {
                win.ContainerContext.RemoveChild(win);
                win.MoveItems(this);
            }
            base.OnDrop(dropZone, dropData);
        }

        #endregion



    }
}